package org.voidness.oje2d.gui;

import java.awt.Dimension;
import java.util.Vector;

import org.voidness.oje2d.Engine;
import org.voidness.oje2d.GLColor;
import org.voidness.oje2d.GLFont;
import org.voidness.oje2d.Utils;

/** A class to represent an abstract graphical widget */
public abstract class Widget {
    /** The widget position in the X axis */
    protected int xPos;

    /** The widget position in the Y axis */
    protected int yPos;

    /** The widget's width */
    protected int width;

    /** The widget's height */
    protected int height;

    /** Is the widget visible? */
    protected boolean visible;

    protected boolean focus;

    /** This widget's parent, another widget */
    protected Widget parent = null;

    /** This widget's listener for events */
    protected Listener listener = null;

    /** The list of child widgets */
    protected Vector<Widget> widgetList = null;

    /** The tooltip value for the widget */
    private String toolTip = null;

    /** The tooltip text font */
    private GLFont toolTipFont = null;

    /** The last time the tooltip was beeing shown */
    private long toolTipLastTime;

    /**
     * The current tooltip time. To compare agains't the last time, so the
     * tooltip appears only after some time
     */
    private long toolTipCurrentTime;

    /** The time interval on which the tooltip is visible */
    private long toolTipTimeInterval;

    /** Whether the tooltip is beeing drawn or not */
    private boolean drawingToolTip;

    /** The tooltip's position */
    private Dimension toolTipPosition = null;

    /**
     * Creates a new widget in mX, mY. This position is relative to the parent
     * widget's position. If the parent is null, it's relative to the screen.
     * 
     * @param mX The X position of the new widget
     * @param mY The Y position of the new widget
     * @param mWidth The widget's width
     * @param mHeight The widget's height
     */
    protected Widget(int mX, int mY, int mWidth, int mHeight) {
        xPos = mX;
        yPos = mY;
        width = mWidth;
        height = mHeight;

        // By default, the widget is visible
        visible = true;

        // Init the widget list
        widgetList = new Vector<Widget>();

        // Has no focus on creation
        focus = false;

        // Is not drawing the tooltip
        drawingToolTip = false;
    }

    /**
     * Another constructor for the widget class.
     * 
     * @param mWidth The widget's width
     * @param mHeight The widget's height
     */
    protected Widget(int mWidth, int mHeight) {
        // The size of this widget
        width = mWidth;
        height = mHeight;

        // By default, the widget is visible
        visible = true;

        // Init the widget list
        widgetList = new Vector<Widget>();

        // Has no focus on creation
        focus = false;

        // Is not drawing the tooltip
        drawingToolTip = false;
    }

    /**
     * Defines this widget's parent.
     * 
     * @param mParent The widget's parent
     */
    public void setParent(Widget mParent) {
        parent = mParent;
    }

    /**
     * Sets the widget's (in)visibility.
     * 
     * @param mVisible True to be visible, false to be invisible
     */
    public void setVisible(boolean mVisible) {
        visible = mVisible;
    }

    /**
     * Is the widget visible?
     * 
     * @return True if yes, false if no
     */
    public boolean isVisible() {
        return visible;
    }

    /**
     * Does this widget have the mouse over it?
     * 
     * @return True if yes, false if no
     */
    public boolean hasMouseOver() {
        int posInX = xPos;
        int posInY = yPos;
        if (parent != null) {
            posInX += parent.getXPos();
            posInY += parent.getYPos();
        }

        if (Engine.getInstance().getMouseX() > posInX
                && Engine.getInstance().getMouseX() < posInX + width
                && Engine.getInstance().getMouseY() > posInY
                && Engine.getInstance().getMouseY() < posInY + height)
            return true;

        return false;
    }

    /**
     * Fires the mouse button pressed event for the widget.
     * 
     * @param mButton The button beeing pressed
     */
    protected void mousePressed(int mButton) {
        // If the click was here...
        if (hasMouseOver()) {
            // Give me focus
            focus = true;

            // And fire the event, if the listener is not null
            if (listener != null)
                listener.mousePressed(mButton);
        } else {
            // Click was somewhere else ... lost focus
            focus = false;
        }

        // Cycle the child widget list and send the mousePressed event
        for (int i = 0; i < widgetList.size(); i++) {
            Widget widget = (Widget) widgetList.elementAt(i);
            widget.mousePressed(mButton);
        }
    }

    /**
     * Fires the mouse button released event for the widget.
     * 
     * @param mButton The button beeing released
     */
    protected void mouseReleased(int mButton) {
        // Return events for this widget
        if (listener != null && hasMouseOver())
            listener.mouseReleased(mButton);

        // Cycle the child widget list and send the mouseReleased event
        for (int i = 0; i < widgetList.size(); i++) {
            Widget widget = (Widget) widgetList.elementAt(i);
            widget.mouseReleased(mButton);
        }
    }

    /**
     * Fires the key pressed event for the widget.
     * 
     * @param mKey The key beeing held down
     * @param mChar The correspondent character
     */
    public void keyPressed(int mKey, char mChar) {
        // Return events for this widget
        if (listener != null && hasFocus())
            listener.keyPressed(mKey);

        // Cycle the child widget list and send the keyPressed event
        for (int i = 0; i < widgetList.size(); i++) {
            Widget widget = (Widget) widgetList.elementAt(i);

            if (widget.hasFocus())
                widget.keyPressed(mKey, mChar);
        }
    }

    /**
     * Fires the key released event for the widget.
     * 
     * @param mKey The key beeing released
     */
    public void keyReleased(int mKey) {
        // Return events for this widget
        if (listener != null && hasFocus())
            listener.keyReleased(mKey);

        // Cycle the child widget list and send the keyReleased event
        for (int i = 0; i < widgetList.size(); i++) {
            Widget widget = (Widget) widgetList.elementAt(i);

            if (widget.hasFocus())
                widget.keyReleased(mKey);
        }
    }

    /**
     * Where is this widget in the X axis? (position relative to the parent)
     * 
     * @return The widget's X position
     */
    public int getXPos() {
        return xPos;
    }

    /**
     * Where is this widget in the Y axis? (position relative to the parent)
     * 
     * @return The widget's Y position
     */
    public int getYPos() {
        return yPos;
    }

    /**
     * What is this widget's width?
     * 
     * @return The widget's width
     */
    public int getWidth() {
        return width;
    }

    /**
     * What is this widget's height?
     * 
     * @return The widget's height
     */
    public int getHeight() {
        return height;
    }

    /**
     * Gives focus to this widget
     * 
     * @param mFocus True for focus
     */
    public void setFocus(boolean mFocus) {
        focus = mFocus;

        if (focus == false) {
            // Cycle the child widget list and lose focus for all
            for (int i = 0; i < widgetList.size(); i++) {
                Widget widget = (Widget) widgetList.elementAt(i);
                widget.setFocus(false);
            }
        }
    }

    /**
     * Returns whether this widget has focus or not
     * 
     * @return True if it has focus, false otherwise
     */
    public boolean hasFocus() {
        return focus;
    }

    /**
     * Adds an event listener to the widget.
     * 
     * @param mListener The listener to add
     */
    public void addListener(Listener mListener) {
        listener = mListener;
    }

    /**
     * Add a child widget.
     * 
     * @param mWidget The widget to add
     */
    public void add(Widget mWidget) {
        mWidget.setParent(this);
        widgetList.add(mWidget);
    }

    /**
     * (Re)defines the tooltip text
     * 
     * @param mText The text to set
     * @param mFont The font to use when drawing the tooltip
     * @param mToolTipTimeInterval The ammount of time the tooltip waits until
     *        it's shown
     * @param mToolTipPosition The tooltip position
     */
    public void setToolTip(String mText, GLFont mFont,
            int mToolTipTimeInterval, Dimension mToolTipPosition) {
        toolTip = new String(mText);
        toolTipFont = mFont;
        toolTipTimeInterval = mToolTipTimeInterval;
        toolTipCurrentTime = Utils.getTime();
        toolTipLastTime = toolTipCurrentTime;
        toolTipPosition = mToolTipPosition;
    }

    /**
     * The default (re)definition of the tooltip. This sets it in the default
     * position (-20, -20)
     * 
     * @param mText The text to set
     * @param mFont The font to use for the text
     * @param mToolTipTimeInterval The ammount of time the tooltip waits until
     *        it's shown
     */
    public void setToolTip(String mText, GLFont mFont, int mToolTipTimeInterval) {
        toolTip = new String(mText);
        toolTipFont = mFont;
        toolTipTimeInterval = mToolTipTimeInterval;
        toolTipCurrentTime = Utils.getTime();
        toolTipLastTime = toolTipCurrentTime;
        toolTipPosition = new Dimension(-20, -20);
    }

    /**
     * Draws the widget's tooltip
     */
    public void drawToolTip() {
        if (toolTip != null && hasMouseOver()) {
            toolTipCurrentTime = Utils.getTime();
            if (toolTipCurrentTime - toolTipLastTime > toolTipTimeInterval) {
                drawingToolTip = true;
                toolTipLastTime = toolTipCurrentTime;
            }
        } else
            drawingToolTip = false;

        if (toolTip != null && hasMouseOver() && drawingToolTip) {
            int posInX =
                    Engine.getInstance().getMouseX() + toolTipPosition.width;
            int posInY =
                    Engine.getInstance().getMouseY() + toolTipPosition.height;

            toolTipFont.setZoom(1.0f);
            Engine.drawQuad(posInX, posInY, toolTip.length()
                    * toolTipFont.getCharSize(), toolTipFont.getCharSize(),
                    GLColor.BLACK);
            Engine.drawQuad(posInX + 1, posInY + 1, toolTip.length()
                    * toolTipFont.getCharSize() - 2,
                    toolTipFont.getCharSize() - 2, GLColor.BLUE);
            toolTipFont.print(toolTip, posInX, posInY, 1.0f, GLColor.WHITE);
        }
    }

    public void setXPos(int mXPos) {
        xPos = mXPos;
    }

    public void setYPos(int mYPos) {
        yPos = mYPos;
    }

    /**
     * All widget extenders must overload the draw method.
     */
    public abstract void draw(GLColor mColor);
}